//#include "littlevgl2rtt.h" 
#include "lvgl.h" 

#include <rtthread.h> 
#include <rtdevice.h>  

#include "libbmp.h"
#include "fcntl.h"
#include "stdio.h"
#include "unistd.h"
#include "dirent.h"
#include "custom.h"

#define ARRAY_NUM(arr)          (sizeof(arr)/sizeof(arr[0]))

#define USE_TEMP_BUFF_DYNAMIC           0
#define USE_DISP_BUFF_AS_TEMP_BUFF      0

#define LV_IMG_WIDTH            280//320//160
#define LV_IMG_HEIGHT           210//240//120
#define LV_IMG_OFFSET           20
static int16_t lv_img_buf[LV_IMG_WIDTH*LV_IMG_HEIGHT];
#if USE_TEMP_BUFF_DYNAMIC
//static int16_t bmpTmpBuff[LV_IMG_WIDTH];
#elif USE_DISP_BUFF_AS_TEMP_BUFF
#else
static int16_t tempBuff[LV_IMG_WIDTH*LV_IMG_HEIGHT];
#endif
static lv_img_dsc_t img_dsc =
{
    .header = 
    {
        .always_zero = 0,
        .w = LV_IMG_WIDTH,
        .h = LV_IMG_HEIGHT,
        .cf = LV_IMG_CF_TRUE_COLOR,
    },
    .data_size = LV_IMG_WIDTH*LV_IMG_HEIGHT*sizeof(lv_color_t),
    .data = (void *)lv_img_buf,
};

void grey_boundary_param_print(void);
void update_file_list()
{
    char fname[64];
    struct dirent *dir_info;
    DIR *dir = opendir("/");
    if(dir == NULL)
    {
        rt_kprintf("open dir failed!\r\n");
        return ;
    }
    while(1)
    {
        dir_info = readdir(dir);
        if(dir_info == NULL)
        {
            break;
        }
        if(dir_info->d_type == DT_REG)
        {
            rt_memcpy(fname, dir_info->d_name, dir_info->d_namlen);
            fname[dir_info->d_namlen] = '\0';
            if(rt_strstr(fname, ".bmp") != NULL)
            {
                lv_custom_files_list_append(fname);
            }
        }
    }
    closedir(dir);
}
extern rt_uint32_t str2hex(const char *str);
/* Declarations */
int readTempValues(int16_t *buff);
void generatePicture(int16_t *buff, int w, int h);
void readMaxTempFromBuff(int16_t *buff, int16_t *obuff, uint16_t *x, uint16_t *y);
void readTempFromBuff(int16_t *buff, struct lv_custom_temp_cursor_data *data, int16_t *obuff, uint16_t num);
void pictureConvert(int16_t *buff, int num, lv_color_t *color_p);
void tempSensorInit(int16_t *buff);
void MLX90640_I2CInit(struct rt_i2c_bus_device *i2c_bus);
/* Various */
#define IRAY_MODE_FLAG_SCREEN_MODE          0x3
#define IRAY_MODE_FLAG_SCREEN_MODE_CAP      0x1
#define IRAY_MODE_FLAG_SCREEN_MODE_REC      0x2
static rt_event_t iray_mode_flag;
//struct rt_completion iray_img_update_cplt;
static struct lv_custom_temp_cursor_data temp_data_cursor[5];
static int16_t temp_data_buff[5];
extern struct rt_device_graphic_info disp_info;
extern rt_device_t lcd_dev;
//
void iray_mode_flag_sync_capture(void)
{
    rt_event_send(iray_mode_flag, IRAY_MODE_FLAG_SCREEN_MODE_CAP);
}
void iray_mode_flag_sync_record(void)
{
    rt_event_send(iray_mode_flag, IRAY_MODE_FLAG_SCREEN_MODE_REC);
}
//
static void color2pixels(lv_color_t *color_buf, bmp_pixel *pixels, int w, int h)
{
    lv_color_t color;
    for(int i=0; i<w*h; i++)
    {
        color = color_buf[i];
        pixels[i] = BMP_PIXEL(LV_COLOR_GET_R(color)<<3,LV_COLOR_GET_G(color)<<2,LV_COLOR_GET_B(color)<<3);
    }
}
static void *iray_picture_encode(const bmp_img *img, int hoffset, void *user_data)
{
    int w = img->img_header.biWidth;
    int16_t *buff = user_data;
    color2pixels((lv_color_t *)&buff[hoffset * w + LV_IMG_OFFSET], img->img_pixels, w, 1);
    return img->img_pixels;
}
void iray_save_picture(char *fname, void *buff)
{
    //char fname[32];
    //rt_sprintf(fname, "%d.bmp", rt_tick_get());
    int fd = open(fname, O_WRONLY | O_CREAT);
    if(fd >= 0)
    {
        bmp_img img = {0};
        bmp_header_init_df(&img.img_header, LV_IMG_WIDTH, LV_IMG_HEIGHT);
        img.img_pixels = malloc(sizeof (bmp_pixel) * LV_IMG_WIDTH);
        if(img.img_pixels)
        {
            bmp_img_write_width(&img, (void *)fd, iray_picture_encode, buff);
        }
        else
        {
            rt_kprintf("pixels is NULL\r\n");
        }
        close(fd);
        bmp_img_free(&img);
    }
}
static void pixels2color(bmp_pixel *pixels, lv_color_t *color_buf, int w, int h)
{
    for(int i=0; i<w*h; i++)
    {
        lv_color_t tmp = LV_COLOR_MAKE(pixels[i].red, pixels[i].green, pixels[i].blue);
        color_buf[i] = tmp;
    }
}
static void *iray_picture_decode(const bmp_img *img, void *buf, int hoffset, void *user_data)
{
    int w = img->img_header.biWidth;
    int16_t *tmpbuff = user_data;
    if(buf)
    {
        pixels2color(img->img_pixels, (lv_color_t *)&tmpbuff[hoffset * w], w, 1);
    }
    else
    {
        return img->img_pixels;
    }
    return NULL;
}
void iray_load_picture(const char *fname, void *buff)
{
    int fd = open(fname, O_RDONLY);
    if(fd >= 0)
    {
        lseek(fd, 0, SEEK_SET);
        bmp_img img = {0};
        bmp_header *header = &img.img_header;
        const enum bmp_error err = bmp_header_read(header, (void *)fd);
        if(err == BMP_OK)
        {
            if((header->biWidth == LV_IMG_WIDTH) && (header->biHeight == LV_IMG_HEIGHT))
            {
                bmp_img_read_width(&img, (void *)fd, iray_picture_decode, buff);
            }
            else
            {
                rt_kprintf("img:%s, %dx%d != %dx%d\r\n", fname, header->biWidth, header->biHeight, LV_IMG_WIDTH, LV_IMG_HEIGHT);
            }
        }
        close(fd);
    }
}
//mlx90640_port
#include <dfs_fs.h>
void iray_img_update_notify(void)
{
    //rt_completion_done(&iray_img_update_cplt);
}
static void iray_camera_entry(void *arg)
{
    for(;;)
    {
        rt_device_t sd = rt_device_find("sd0");
        if(sd != RT_NULL)
        {
            if(dfs_mount("sd0", "/", "elm", 0, NULL) == 0)
            {
                break;
            }
        }
        rt_kprintf("mount sd0 try again...\r\n");
        rt_thread_mdelay(1000);
    }
    MLX90640_I2CInit(arg);
    tempSensorInit((void*)lv_img_buf/*tempValuex100Buff*/);
    //rt_tick_t tick = rt_tick_get();
    rt_thread_t lvgl = rt_thread_find("LVGL");
    if(lvgl == NULL)
    {
        rt_kprintf("lvgl thread not found.\r\n");
    }
    int num;
    while(1)
    {
        switch(lv_custom_get_current_screen_index())
        {
            case 0:
            {
                rt_uint32_t flag;
                if(RT_EOK == rt_event_recv(
                    iray_mode_flag, 
                    IRAY_MODE_FLAG_SCREEN_MODE, 
                    RT_EVENT_FLAG_OR|RT_EVENT_FLAG_CLEAR, 
                    rt_tick_from_millisecond(100), 
                    &flag))
                {
                    if(flag == IRAY_MODE_FLAG_SCREEN_MODE_CAP)
                    {
                        rt_thread_mdelay(100);
                        #if USE_TEMP_BUFF_DYNAMIC
                        const int size = LV_IMG_WIDTH*LV_IMG_HEIGHT*sizeof(int16_t);
                        int16_t *buff = rt_malloc(size);
                        if(buff)
                        {
                            if(0 == lv_custom_take_snapshot(buff, size))
                            {
                                char fname[32];
                                rt_sprintf(fname, "%d.img", rt_tick_get());
                                iray_save_picture(fname, buff);
                            }
                            rt_free(buff);
                        }
                        #else
                        char fname[32];
                        rt_sprintf(fname, "%d.bmp", rt_tick_get());
                        lv_obj_t * act_scr = lv_scr_act();
                        lv_disp_t * d = lv_obj_get_disp(act_scr);
                        lv_disp_draw_buf_t *draw_buf = lv_disp_get_draw_buf(d);
                        uint16_t *dbuff = (uint16_t *)draw_buf->buf1;
                        if(dbuff)
                        {
                            #if USE_DISP_BUFF_AS_TEMP_BUFF
                            iray_save_picture(fname, fbuff);
                            #else
                            int w = disp_info.width;
                            for(int i=0; i<LV_IMG_HEIGHT; i++)
                            {
                                rt_memcpy(&tempBuff[i*LV_IMG_WIDTH], &dbuff[i * w + ((w-LV_IMG_WIDTH)/2)], LV_IMG_WIDTH*2);
                            }
                            iray_save_picture(fname, tempBuff);
                            #endif
                        }
                        #endif
                    }
                }
                else
                {
                    #if USE_TEMP_BUFF_DYNAMIC
                    int16_t *buff = rt_malloc(LV_IMG_WIDTH*LV_IMG_HEIGHT*sizeof(int16_t));
                    if(buff)
                    {
                        if(readTempValues(buff) == 0)
                        {
                            generatePicture(buff, LV_IMG_WIDTH, LV_IMG_HEIGHT);
                            num = lv_custom_temp_cursor_get_pos(temp_data_cursor, 0, ARRAY_NUM(temp_data_cursor));
                            readMaxTempFromBuff(buff, temp_data_buff, &temp_data_cursor->x, &temp_data_cursor->y);
                            lv_custom_temp_cursor_set_pos(temp_data_cursor, -1, 1);
                            if(num > 1)
                            {
                                readTempFromBuff(buff, &temp_data_cursor[1], &temp_data_buff[1], num-1);
                            }
                            lv_custom_temp_cursor_set_temp(temp_data_buff, num);
                            pictureConvert(buff, LV_IMG_WIDTH * LV_IMG_HEIGHT, (void*)lv_img_buf);
                            //rt_completion_wait(&iray_img_update_cplt, 0);
                            lv_custom_img_iray_update();
                            //rt_completion_wait(&iray_img_update_cplt, RT_WAITING_FOREVER);
                        }
                        rt_free(buff);
                    }
                    #else
                    #if USE_DISP_BUFF_AS_TEMP_BUFF
                    int16_t *tempBuff = (int16_t *)disp_info.framebuffer;
                    #endif
                    if(readTempValues(tempBuff) == 0)
                    {
                        generatePicture(tempBuff, LV_IMG_WIDTH, LV_IMG_HEIGHT);
                        num = lv_custom_temp_cursor_get_pos(temp_data_cursor, 0, ARRAY_NUM(temp_data_cursor));
                        readMaxTempFromBuff(tempBuff, temp_data_buff, &temp_data_cursor->x, &temp_data_cursor->y);
                        lv_custom_temp_cursor_set_pos(temp_data_cursor, -1, 1);
                        if(num > 1)
                        {
                            readTempFromBuff(tempBuff, &temp_data_cursor[1], &temp_data_buff[1], num-1);
                        }
                        lv_custom_temp_cursor_set_temp(temp_data_buff, num);
                        pictureConvert(tempBuff, LV_IMG_WIDTH * LV_IMG_HEIGHT, (void*)lv_img_buf);
                        //rt_completion_wait(&iray_img_update_cplt, 0);
                        lv_custom_img_iray_update();
                        //rt_completion_wait(&iray_img_update_cplt, RT_WAITING_FOREVER);
                    }
                    #endif
                }
            }
                break;
            case 1:
            {
                rt_thread_mdelay(100);
            }
                break;
            case 2:
            {
                rt_thread_mdelay(100);
            }
                break;
            default:
            {
                rt_thread_mdelay(100);
            }
                break;
        }
    }
}
void iray_camera_init(const char *bus)
{
    struct rt_i2c_bus_device *i2c_bus = (void *)rt_device_find(bus);
    if(i2c_bus == NULL)
    {
        rt_kprintf("bus %s not found.\r\n", bus);
        return ;
    }
    if(RT_EOK != rt_device_open((void *)i2c_bus, RT_DEVICE_FLAG_RDWR))
    {
        rt_kprintf("fail to open bus %s.\r\n", bus);
        return ;
    }
    //rt_completion_init(&iray_img_update_cplt);
    iray_mode_flag = rt_event_create("irayf", RT_IPC_FLAG_FIFO);
    if(iray_mode_flag == NULL)
    {
        return ;
    }
    lv_custom_img_iray_descriptor(&img_dsc);
    lv_custom_files_list_style_init();
    rt_thread_t tid = rt_thread_create("iray", iray_camera_entry, i2c_bus, 8192, 11, 0);
    if(tid)
    {
        rt_thread_startup(tid);
    }
    else
    {
        rt_kprintf("create iray failed!\r\n");
    }
}
static int iray_camera_env_init(void)
{
    iray_camera_init("sci2c2");
    return 0;
}
INIT_ENV_EXPORT(iray_camera_env_init);
//INIT_APP_EXPORT(rt_lvgl_demo_init); 
static int rt_lvgl_iray_camera_cmd(int argc, char *argv[])
{
    rt_err_t ret = RT_EOK;
    rt_thread_t thread = RT_NULL;

    if(rt_strcmp(argv[1], "start") == 0)
    {
        if(argc > 2)
        {
            const char *bus = argv[2];
            iray_camera_init(bus);
        }
    }

    return RT_EOK; 
}
MSH_CMD_EXPORT_ALIAS(rt_lvgl_iray_camera_cmd, lvgl_iray,lvgl iray);


